查看原文
其他

31万弹幕大军都推荐你去看的《山海情》,是怎样一部最搞笑最土味的扶贫剧!?

才哥 可以叫我才哥 2021-10-08




大家好,今天我们来看看这部由豆瓣7万人评分高达9.4分的开年大剧《山海情》吧。

也就是在1月24日该剧迎来了大结局,我们爬取腾讯视频全23集共31.79万条弹幕,看看大家都在聊什么!

《山海情》讲述了二十世纪九十年代以来,西海固的人民和干部们响应国家扶贫政策的号召,完成异地搬迁,在福建的对口帮扶下,通过辛勤劳动和不懈探索,将风沙走石的“干沙滩”建设成寸土寸金的“金沙滩”的故事。


目录:

  • 1. 数据预览

  • 2. 各集弹幕数

  • 3. 弹幕词云

    • 3.1. 全部弹幕词云

    • 3.2. 去掉人名后的弹幕词云

    • 3.3. 主要角色关联词云

  • 4. 弹幕情感分析

    • 4.1. 弹幕抽样情感分析

    • 4.2. 每集点赞第1的弹幕情感分析

  • 5. 爬虫代码

1. 数据预览

爬虫部分代码较多,我们放在最后哈。完整代码及弹幕数据文件大家亦可 在公众号 后台 回复 山海情 获取。

预览数据中,相关字段说明如下:

commentid :弹幕唯一id
content :弹幕内容
upcount :点赞数
timepoint :所属集对应弹幕发送时间(s)
opername :弹幕 用户昵称
uservip_degree :弹幕用户vip等级
集 :所属集数

In [1]: df.head()
Out[1]: 
             commentid    content  upcount  ...  opername uservip_degree  集
0  6754755585141912011  我来这么早?没人么       25  ...       NaN              5  1
1  6754755922809562416         第一        0  ...       NaN              4  1
2  6754758012762783182       来了来了       10  ...       NaN              3  1
3  6754758030261780084        打卡了       11  ...       NaN              5  1
4  6754758276425465450     宁夏人来啦!       17  ...         寒              0  1

[5 rows x 7 columns]

In [2]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex:
 317927 entries, 0 to 317926
Data columns (total 7 columns):
 #   Column          Non-Null Count   Dtype 
---  ------          --------------   ----- 
 0   commentid       317927 non-null  int64 
 1   content         317927 non-null  object
 2   upcount         317927 non-null  int64 
 3   timepoint       317927 non-null  int64 
 4   opername        126947 non-null  object
 5   uservip_degree  317927 non-null  int64 
 6   集               317927 non-null  int64 
dtypes: int64(5), object(2)
memory usage: 17.0+ MB

In [3]: df[['upcount','uservip_degree']].describe()
Out[3]: 
             upcount  uservip_degree
count  317927.000000    317927.00000
mean        1.893925         0.77157
std         3.460646         1.69589
min         0.000000         0.00000
25%         0.000000         0.00000
50%         1.000000         0.00000
75%         2.000000         0.00000
max       102.000000         8.00000

2. 各集弹幕数

全剧一共23集,共有317,927条弹幕。

In [4]: danmu = df.groupby('集')['commentid'].count().to_frame('弹幕数').reset_index()
山海情

绘图代码:

# 引入需要用到的库
from pyecharts.globals import CurrentConfig, NotebookType
CurrentConfig.NOTEBOOK_TYPE = NotebookType.JUPYTER_LAB
from pyecharts import options as opts
from pyecharts.commons.utils import JsCode
from pyecharts.charts import *
# 绘制租房类型分布
bar = (
    Bar(init_opts=opts.InitOpts(theme='dark', width='1000px'))
    .add_xaxis(danmu.集.to_list())
    .add_yaxis('弹幕数', danmu['弹幕数'].to_list())
    .set_global_opts(legend_opts=opts.LegendOpts(is_show=True),
                     xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=45)),
                     title_opts=opts.TitleOpts(title="《山海情》各集弹幕数",
                                         subtitle='数据采集日期:2021年1月'),
                    )   
)
bar.load_javascript()
bar.render_notebook()

3. 弹幕词云

本部分我们用jieba进行分词 和 stylecloud进行词云绘制,由于stylecloud在制作词云时的背景只能用它指定的哪些,我们需要进行相关调整后使其能自定义背景图。

3.1. 全部弹幕词云

我们用全部的弹幕分词后绘制词云(以福建省行政区域为背景)如下图所示,咱们可以发现,在本剧中最受用户热议的是得宝、水花、老师和麦苗这四个角色。

全部弹幕

3.2. 去掉人名后的弹幕词云

我们去掉人名(将剧中的人名添加到停用词)的弹幕分词后绘制词云(以宁夏省行政区域为背景)如下图所示,可以发现 大家都觉得该剧特别的真实。评价基本全是正向的,表示 喜欢的,夸好看的,会让人想起自己的小时候,以及剧中也充满着让人哈哈哈的桥段(从郭京飞饰演的刘金山登场开始,那一口胡建普通话简直太逗了)。

去掉人名

3.3. 主要角色关联词云

马得宝,马得福的弟弟,作为弹幕最爱角色,是在他同龄的几个小伙伴中的领导者,年轻气盛有勇有谋,从策划逃村时给小伙伴们清晰地分工。挨着父亲的鞭子仍喊着“想要走出去”,他与麦苗一家在移民新区重逢时有条不紊地忙前忙后,再到带着丢了工作的小伙伴进程谋职时的坚定果断,他身上都透露出超越年龄的担当与智慧,在闽宁镇建设中起到了先锋带头作用。


马得福,本剧的主角,奋斗在第一线的基层干部,从农校毕业的第一件事就是追回家乡涌泉村里“逃跑”的吊庄户。从苦口婆心地劝返吊庄户、帮助村民完成“吊庄移民”工作,到软磨硬泡给移民村通电,再到之后东西协作扶贫政策出台后,带领村民们共同走上致富的康庄大道。


李水花,从小与马得福青梅竹马,情投意合。父亲因为一头驴、两只羊、两笼鸡的彩礼就把她“卖”到邻村。她内心坚定乐观,永远笑对生活和未知,不愿意认命的她选择坐火车出逃反抗命运。她在福建教授凌一农的帮助下,在自家庭院里种起了双孢菇。在闽宁镇建设中浇灌希望,追寻光芒。


白麦苗,白校长的女儿,和得宝的爱情非常纯。因为母亲早逝,她对父亲心存隔阂,从小脾气有些古怪,跟得宝、水旺、尕娃一起长大。她天资聪颖,直言快语,内心好强不服输,敢于走出去,愿意尝试新的生活方式,想要靠自己勤劳的双手开创新世界。她积极响应国家政策,到福建莆田打工,她在工作中帮助同乡姐妹们,且细心努力,成为厂里女工中的先进典范。


白校长,教书育人的白校长,憨厚朴实,热情、用心良苦、无奈和不被理解的痛苦,但他一生为教育事业做贡献,坚守着自己教书育人的事业。他是一位来到宁夏西海固地区支教的老师,他已经扎根于涌泉村了,和西海固的人口音虽然略有不同,但早已把那里当成了自己的家,并且在那里娶妻生子,似乎成为地地道道的涌泉村人了。

4. 弹幕情感分析

关于弹幕情感分析,我们这里采用的是腾讯云-自然语言处理中的篇章分析相关接口。

由于弹幕总量较多,这里仅进行两类文本取样分析:①抽样;②每集点赞第1的弹幕

腾讯情感分析接口代码如下:

前提需要将API Python SDK 安装到您的环境中

pip install --upgrade tencentcloud-sdk-python
import json
from tencentcloud.common import credential
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.nlp.v20190408 import nlp_client, models

def get_sentiment(text):
    try
        cred = credential.Credential("你的key""你的secret"
        httpProfile = HttpProfile()
        httpProfile.endpoint = "nlp.tencentcloudapi.com"
    
        clientProfile = ClientProfile()
        clientProfile.httpProfile = httpProfile
        client = nlp_client.NlpClient(cred, "ap-guangzhou", clientProfile) 
    
        req = models.SentimentAnalysisRequest()
        params = {
            "Text": text,
            "Mode""3class"
        }
        req.from_json_string(json.dumps(params))
    
        resp = client.SentimentAnalysis(req) 
        j = json.loads(resp.to_json_string())
        
        return j['Sentiment']
    except TencentCloudSDKException as err: 
        print(err)

举例:

In [4]: get_sentiment(text = '你吃过了吗')
Out[4]: 'neutral'

In [5]: get_sentiment(text = '你好')
Out[5]: 'positive'

In [6]: get_sentiment(text = '你很烦')
Out[6]: 'negative'

4.1. 弹幕抽样情感分析

抽样10条弹幕,做简单的情感分析,不过好像并不是很准,哈哈。

In [7]: sample = df.sample(10)

In [8]: sample
Out[8]: 
                  commentid          content  ...  uservip_degree   集
20206   6755378371346595774            演的太好了  ...               0   2
174127  6757131757407377261     这样的县长应该提起来重用  ...               1  13
15589   6755432423347440299            给了点路费  ...               0   1
314161  6759132523517160753           得花这过分了  ...               2  23
19362   6758001303567706465            96年路过  ...               0   2
147508  6756875386320615867       现在上海15左右一斤  ...               0  11
247891  6758228650422150608     我要早点遇上这老师该多好  ...               0  18
95010   6757574373873484314          南方有蟑螂正常  ...               0   6
34339   6755052757532740869  九十年代有这么穷吗?七十年代吧  ...               4   2
4332    6755503063420138319          都是西部演员啊  ...               0   1

[10 rows x 7 columns]

In [9]: data = sample[['content']]

In [10]: data['情感倾向'] = data['content'].apply(get_sentiment)

In [11]: data
Out[11]: 
                content      情感倾向
20206             演的太好了  positive
174127     这样的县长应该提起来重用   neutral
15589             给了点路费  positive
314161           得花这过分了  negative
19362             96年路过   neutral
147508       现在上海15左右一斤   neutral
247891     我要早点遇上这老师该多好  positive
95010           南方有蟑螂正常  negative
34339   九十年代有这么穷吗?七十年代吧   neutral
4332            都是西部演员啊   neutral

4.2. 每集点赞第1的弹幕情感分析

通过排序然后分组取第一个值,即可获得每集点赞最多的弹幕。

获取每集点赞最多的弹幕
In [12]: data = df.sort_values('upcount', ascending=False).groupby('集', as_index=False).first()
    ...: data['情感倾向'] = data['content'].apply(get_sentiment)
弹幕情感

5. 爬虫代码

对于单集弹幕的爬虫比较简单,直接获取真实的弹幕url地址即可。

对于全部剧集的弹幕爬虫,我们需要观察单集的弹幕url地址规律然后构建全部剧集的弹幕url地址列表。这其中比较关键的点在 target_id的获取,具体大家阅读代码练习研究吧。这里我就先不做详细说明了。

import pandas as pd
import requests
from bs4 import BeautifulSoup
import re
import time
    
def get_suf(url):
    # 获取target后缀列表
    url_suf = url
    r_suf = requests.get(url_suf)
    # 乱码修正
    r_suf.encoding = r_suf.apparent_encoding
    soup = BeautifulSoup(r_suf.text, 'lxml')
    # 获取全部剧集 所在的节点 class="mod_episode"
    sellList = soup.find(class_="mod_episode")
    # 获取全部剧集节点列表 _stat="videolist:click"
    lis = sellList.find_all('span',_stat="videolist:click")
    # 选取第一个target_id 后缀
    # div = lis[0].get('id')
    sufs = []
    for li in lis:
        suf = li.get('id')
        sufs.append(suf)
        
    return sufs


def get_pre(sufs):
    # 获取target_id列表
    url_pre = 'https://access.video.qq.com/danmu_manage/regist?'
    # 参数
    parames = {
        'vappid'97767206,
        'vsecret''c0bdcbae120669fff425d0ef853674614aa659c605a613a4',
        'raw'1,
        }
    # payload
    target_ids = []
    for suf in sufs:
        payload = {
            "wRegistType":2,
            "vecIdList":[suf],
            "wSpeSource":0,
            "bIsGetUserCfg":1,
            "mapExtData":{
                suf:{
                    "strCid":"mzc0020009e7qbj",
                    "strLid":""
                    }
                }
            }
        
        r_pre = requests.post(url_pre, params = parames, json = payload)
        text = r_pre.text
        target_id = re.findall(r'targetid=(.*?)"',text)[0]
        target_ids.append(target_id)
        
    return target_ids


def get_danmu(target_ids):
    # 获取弹幕信息
    headers = {
        "User-Agent""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36",
        }
    
    url = 'https://mfm.video.qq.com/danmu?'
    df_list = []
    for i,target_id in enumerate(target_ids):
        time.sleep(1)
        print(f'正在爬取第 {i+1} 集数据')
        comments = []
        for page in range(1,100):
            # page = 1 
            parames = {
                    'otype''json',
                    # 'callback': 'jQuery1910568921143306111_1611479965774',
                    'target_id': target_id,
                    # 'session_key': '30279,0,1611479967',
                    'timestamp'15+30*(page-1),
                    }
            try:
                r = requests.get(url,headers=headers,params=parames)
            except:
                r = requests.get(url,headers=headers,params=parames,verify=False)

            j = r.json()
            comment = j['comments']
            # if len(comment) == 0:
            #     break
            # else:
            comments.extend(comment)
            print(f'第 {i+1} 集 已爬取 {len(comments)} 条弹幕')
        df = pd.DataFrame(comments)
        df['集'] = i+1
        df_list.append(df)
        
    data = pd.concat(df_list)
    data = data[['commentid''content''upcount',
                 'timepoint''opername''uservip_degree''集']]
    return data

if __name__ ==  '__main__':
    # 忽略警告信息
    requests.packages.urllib3.disable_warnings()
    url_suf = 'https://v.qq.com/x/cover/mzc0020009e7qbj.html'  
    # 获取后缀
    sufs = get_suf(url_suf)
    # 获取target_ids
    target_ids = get_pre(sufs)
    
    # 获取全部数据
    df = get_danmu(target_ids)
- 往期推荐 -
Python爬虫 | 华晨宇未婚生女?我们爬取今日头条1.5万条评论,看看吃瓜群众怎么看?
Python爬虫 | 爬虫基础入门看这一篇就够了
Python基础 | 新手学Python时常见的语法错误和异常
- END -



: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存